package com.hibegin.http.server;

import com.hibegin.http.server.config.RequestConfig;
import com.hibegin.http.server.config.ResponseConfig;
import com.hibegin.http.server.config.ServerConfig;
import com.hibegin.http.server.impl.HttpRequestDecoder;
import com.hibegin.http.server.util.ServerInfo;
import com.hibegin.http.server.web.Interceptor;
import com.szwistar.common.os.FileUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.Thread.State;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class SimpleWebServer {
    private static final Logger LOG = LoggerFactory.getLogger(SimpleWebServer.class);

    protected ServerConfig serverConfig;
    protected RequestConfig requestConfig;
    protected ResponseConfig responseConfig;
    private List<Interceptor> interceptors;

    // 多路复用器主工作线程
	private Thread mainThread;
    // 多路复用器
    private Selector selector;
    // 服务器 ServerSocketChannel
    private ServerSocketChannel serverChannel;

    // 请求处理任务执行器
    private Executor channelTaskExecutor;

    public ServerConfig getServerConfig() { return serverConfig; }
    public RequestConfig getRequestConfig() { return requestConfig; }
	public ResponseConfig getResponseConfig() { return responseConfig; }
    public List<Interceptor> getInterceptors() { return interceptors; }

    /**
     * 创建一个服务器
     * @param serverConfig 服务器配置参数；如果为空，则使用默认配置参数
     * @return 服务器
     */
    public static SimpleWebServer create(ServerConfig serverConfig) throws Exception {
    	// 如果没有配置服务器参数，则创建默认的配置
        if (serverConfig == null) {
            serverConfig = new ServerConfig(null, null, null);
        }

        if (serverConfig.isSsl()) {
        	return new SimpleHttpsWebServer(serverConfig);
        } else {
        	return new SimpleWebServer(serverConfig);
        }
    }

    protected SimpleWebServer(ServerConfig serverConf) throws Exception {
        LOG.info("初始化 {}({}) ...", ServerInfo.getName(), ServerInfo.getVersion());

    	// 如果未指定服务器配置，则创建一个默认的
        if (serverConf == null) { serverConf = new ServerConfig(null, null, null); }
        this.serverConfig = serverConf;

    	// 创建 HTTP 请求配置
        requestConfig = new RequestConfig();
        requestConfig.setIsSsl(serverConfig.isSsl());
        requestConfig.setCharSet(serverConfig.getCharset());
        requestConfig.setDisableCookie(serverConfig.isDisableCookie());
        requestConfig.setMaxRequestBodySize(serverConfig.getMaxRequestBodySize());

    	// 创建 HTTP 响应配置
        this.responseConfig = new ResponseConfig();
        responseConfig.setCharSet(serverConfig.getCharset());
        responseConfig.setSupportGzip(serverConfig.isSupportGzip());
        responseConfig.setDisableCookie(serverConfig.isDisableCookie());

    	// 添加请求拦截器
        interceptors = new ArrayList<Interceptor>();
        for (Class<? extends Interceptor> interceptorClazz : serverConfig.getInterceptors()) {
            Interceptor interceptor = interceptorClazz.getDeclaredConstructor().newInstance();
            interceptors.add(interceptor);
        }

        channelTaskExecutor = new ThreadPoolExecutor(
           	serverConfig.getDecodeThreadCoreCount(),
           	serverConfig.getRequestThreadMaxCount(),
    		1, TimeUnit.SECONDS,
    		new ArrayBlockingQueue<Runnable>(100),
    		new ThreadFactory() {
                @Override public Thread newThread(Runnable task) {
                    return new Thread(task, "SimpleWebServer-channel-task");
                }
            }
    	);

        mainThread = new Thread("SimpleWebServer-main") {
            @Override public void run() {
                selectorMainLoop();
            }
        };
    }

    /**
     * 启动服务器
     */
    public void start() throws Exception {
    	// 如果已经启动，则直接返回
    	if(selector != null) { return; }

        // 创建多路复用器
        selector = Selector.open();

        // 创建服务器 ServerSocketChannel
        serverChannel = ServerSocketChannel.open();
        // 打开服务器端口
        serverChannel.socket().bind(new InetSocketAddress(serverConfig.getHost(), serverConfig.getPort()));
        // 配置为非阻塞
        serverChannel.configureBlocking(false);
        // 添加到多路复用器
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        LOG.info(ServerInfo.getName() + " listening on port -> " + serverConfig.getPort());

        // 启动服务器主线程
        mainThread.start();
    }

    /**
     * 服务器是否正在运行，可以接收请求
     */
    public boolean isRunning() {
    	if(mainThread == null) { return false; }
    	State state = mainThread.getState();
    	return state != State.NEW && state != State.TERMINATED;
    }

    /**
     * 停止服务器
     */
    public void stop() {
    	LOG.info("正在停止 Web 服务器 ...");
    	if(selector == null) { return; }
    	try { selector.close(); selector = null; } catch (Exception e) {}
    	try { mainThread.interrupt(); } catch (Exception e) {}
    	try { serverChannel.close(); serverChannel = null; } catch (Exception e) {}
    }

    // 多路复用器主循环
    protected void selectorMainLoop() {
        // 多路复用器主循环
        while (selector.isOpen()) {
        	// 有事件产生的连接列表(迭代器)
        	Iterator<SelectionKey> iterator = null;

            try {
                // 休眠一下，以减少 CPU 占用
                Thread.sleep(1);

            	// 等待连接事件产生
                int num = selector.select();
                if(num < 1) { continue; }

                // 取得有事件产生的连接列表(迭代器)
                iterator = selector.selectedKeys().iterator();

                // 逐个连接处理
                while(iterator.hasNext()) {
                    try {
                    	// 取出并移除连接
                        SelectionKey key = iterator.next(); iterator.remove();
                        // 处理连接事件
                        keyEventProcess(key);
					} catch (Exception e) {
						LOG.error("处理连接事件遇到错误：{}", e.getMessage());
					}
                }
            } catch(ClosedSelectorException e) {
                LOG.error("服务器被关闭！");
            	break;
            } catch(Exception e) {
                LOG.error("", e);
            }
        }
    }

    // 处理连接事件
    protected void keyEventProcess(SelectionKey key) {
        if(!key.isValid()) { return; }

        if (key.isAcceptable()) {
        	// 是服务器主连接，有客户端要连接进来
            ServerSocketChannel server = (ServerSocketChannel) key.channel();
            SocketChannel channel = null;
            try {
            	// 接受客户端连接
            	channel = server.accept();
                if (channel != null) {
                    //LOG.debug("接受客户端连接：{}", decoder);
                	// 配置连接为非阻塞
                    channel.configureBlocking(false);

                	// 创建对应的解码器
                	HttpRequestDecoder decoder = new HttpRequestDecoder(this, channel);
                    // 把连接添加到多路复用器（同时绑定对应的解码器）
                	channel.register(selector, SelectionKey.OP_READ, decoder);
                }
            } catch (IOException e) {
                LOG.error("接受客户端连接失败！", e);
                if (channel != null) {
                    FileUtils.close(channel);
                }
            }
        }
        else if(key.isReadable()) {
        	// 是客户端连接的事件，读取数据
            HttpRequestDecoder decoder = (HttpRequestDecoder)key.attachment();
            // 调用解码器解码
            decoder.runDecodeTask();
        }
    }

    // 在通道请求处理线程池中执行任务
    public void execChannelTask(Runnable task) {
        channelTaskExecutor.execute(task);
    }

    /**
     * 简单封装通道数据读取函数，以便处理其它链路层协议，如 SSL
     * @param channel 要读取的通道
     * @param data 数据缓冲区
     * @see SocketChannel#read(ByteBuffer)
     */
    public int read(SocketChannel channel, ByteBuffer data) throws IOException {
		return channel.read(data);
    }

    /**
     * 简单封装通道数据写入函数，以便处理其它链路层协议，如 SSL
     * @param channel 要写入的通道
     * @param data 数据缓冲区
     * @return 发送的字节数
     * @see SocketChannel#read(ByteBuffer)
     */
	public int write(SocketChannel channel, ByteBuffer data) throws IOException {
		int len = 0;
		while(data.hasRemaining() && channel.isConnected()) {
			len += channel.write(data);
		}
		return len;
	}
}
